Skip to main content

Example 4 - Scene extensions

This example showcases how to extend the scene with custom Three.js components.

Reference: Example 4 - Scene extensions

Key Features

  • Integration with React Three Fiber
  • Custom animated 3D object (spinning cube)
  • Viewport-specific rendering
  • Scene extension architecture
  • Multiple viewports with selective content

Code Example

Below is an example implementation of a spinning cube that is rendered selectively in specific viewports. The cube rotates continuously and demonstrates how to extend the scene with custom Three.js components.

import { Stack } from "@mui/system";
import { useFrame, useThree } from "@react-three/fiber";
import { useRef } from "react";
import type { Mesh } from "three";

import { PlySample } from "../../data/scans";
import { useViewId } from "../../helpers/ViewContext";
import { Viewport } from "../../ui/Viewport";
import { Viewer } from "../../Viewer";
import { PerspectiveView } from "../../views/PerspectiveView";

// Use a relative path string instead of importing the file content
const factoryPath = PlySample["FACTORY"].url;

export function SpinningCube() {
const meshRef = useRef<Mesh>(null!);
const viewId = useViewId();
const invalidate = useThree((s) => s.invalidate);

// Rotate the cube on each frame
useFrame((state, delta) => {
if (meshRef.current) {
meshRef.current.rotation.x += delta;
meshRef.current.rotation.y += delta;

// Invalidate the scene to update the view - otherwise the view will not update.
// Try removing this line to see the difference.
invalidate();
}
});

// Only render the cube in the "3d-2" view, not in the "3d" view.
if (viewId === "3d") return null;

return (
<mesh ref={meshRef}>
<boxGeometry args={[30, 30, 30]} />
<meshStandardMaterial color="orange" />
</mesh>
);
}

function App() {
return (
<Viewer
sx={{
background: "transparent",
position: "absolute",
inset: 0,
zIndex: 0,
}}
sceneExtension={
<>
{/* Add the SpinningCube component to the scene */}
{/* Please note - the extensions here will be added to all views. */}
{/* If you want to add it to a specific view, you need to check the viewId inside the component. */}
{/* See the SpinningCube component for an example. */}
<SpinningCube />
</>
}
objects={{
example: {
url: factoryPath,
excludeInViews: ["3d-2"],
},
}}
>
<Stack sx={{ width: "100%", height: "100%", flexDirection: "row" }}>
<Viewport props={{}} name={"3d"} component={PerspectiveView} />
<Viewport props={{}} name={"3d-2"} component={PerspectiveView} />
</Stack>
</Viewer>
);
}

export default App;

Explanation

  1. SpinningCube Component: This component creates a spinning cube using Three.js. It uses useFrame to update the cube's rotation on each frame and selectively renders the cube based on the viewId.

  2. Viewer Component: The Viewer component is extended with the SpinningCube component. The sceneExtension prop allows adding custom components to the scene.

  3. Multiple Viewports: Two viewports (3d and 3d-2) are defined. The spinning cube is only rendered in the 3d-2 viewport, demonstrating viewport-specific rendering.

  4. Scene Invalidation: The invalidate function ensures the scene updates correctly when the cube rotates.

This example highlights how to extend and customize scenes in a React Three Fiber application.